package com.supervision.manage.service

import com.fly.base.core.Core
import com.fly.base.enums.StatusEnum
import com.fly.base.exception.AuthFailException
import com.fly.base.redis.RedisKey
import com.fly.base.redis.RedisService
import com.fly.base.util.DateFormatUtil
import com.fly.base.util.Util
import com.supervision.domain.exception.NotAllowedException
import com.supervision.manage.common.Const
import com.supervision.manage.common.DXKey
import com.supervision.domain.model.Admin
import com.supervision.domain.model.Token
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.util.DigestUtils
import java.util.*
import java.util.concurrent.Executors

@Component("asuTokenService")
class TokenService {

    @Autowired
    lateinit var redisService: RedisService

    private val executorService = Executors.newFixedThreadPool(8)

    fun getToken(token: String): Token? {
        if (token.isEmpty()) {
            return null
        }
        val tokenModel = redisService.client.use { r ->
            val tokenModelString: String? = r.get(RedisKey.token(token))
            if (tokenModelString != null && tokenModelString.isNotEmpty()) {
                Util.jsonDecode(tokenModelString, Token::class.java)
            } else {
                null
            }
        }

        if (tokenModel != null) {
            return tokenModel
        }

        val dbToken = Core.Q().findByCols(Token::class.java, "token", token, "status", StatusEnum.YES.code)
        if (dbToken == null) {
            return dbToken
        }

        this.cacheToken(dbToken)

        return dbToken
    }

    private fun cacheToken(token: Token) {
        redisService.cache(RedisKey.token(token.token), Util.jsonEncode(token), 86400)
    }

    private fun cleanCacheToken(token: String) {
        redisService.client.use { r ->
            r.del(RedisKey.token(token))
        }
    }

    fun create(type: Int, userId: Long, userType: Int, status: Int, expireTime: Long): Token {
        val token = Token()
        token.type = type
        token.userId = userId
        token.userType = userType
        token.status = status
        token.expireTime = expireTime
        token.token = makeToken()

        token.save()

        executorService.execute {
            token.persist()
        }

        this.cacheToken(token)

        return token
    }

    private fun makeToken(): String {
        redisService.client.use { client ->
            val count = client.incr(RedisKey.todayTokenCount())
            val token = System.currentTimeMillis().toString() + ":" + Math.random() + ":" + count + ':' + UUID.randomUUID().toString()
            return DigestUtils.md5DigestAsHex(token.toByteArray())
        }
    }

    fun makeTokenInvalid(token: String) {
        val tokenModel = Core.Q().findByCol(Token::class.java, "token", token)
        if (tokenModel != null) {
            tokenModel.status = StatusEnum.DEFAULT.code
            tokenModel.save()
        }

        this.cleanCacheToken(token)
    }

    fun veryApiUserAuth(userId: Long, requestUri: String) {
        val admin = Admin.findById(userId) ?: throw NotAllowedException("您没有访问权限")
        // 1为超级管理员，直接放行
        if (admin.roleId != Const.SUPER_ADMIN_ID) {
            val apiUri = requestUri.replace("/manage/1/", "")
            redisService.client.use { r ->
                val redisKey = DXKey.getKeyOfApiListAll()
                val sismember = r.sismember(redisKey, apiUri)
                if (sismember) {
                    val keyOfRoleApiList = DXKey.getKeyOfRoleApiList(admin.roleId)
                    if (!r.sismember(keyOfRoleApiList, apiUri)) {
                        throw NotAllowedException("您没有访问权限")
                    }
                }
            }
        }
    }

    // 检查token的有效期，目前为无操作半小时
    fun checkExpire(tokenModel: Token) {
        val nowTime = DateFormatUtil.time()
        if (nowTime - tokenModel.updateTime > 1800) {
            throw AuthFailException("Token 已过期")
        }
    }
}